package com.bruce.spring.http.service.cgroup;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @author bruce 2025/10/17 18:12
 */
public abstract class CgroupV1BaseProvider implements CpuUsageProvider {

    private static final String CPU_PATH = "/sys/fs/cgroup/cpu,cpuacct/";

    protected final String CPU_AcctUsagePath = CPU_PATH + "cpuacct.usage"; // 高频读取：复用通道
    protected final String CPU_QuotaPath = CPU_PATH + "cpu.cfs_quota_us"; // 初始化读取：一次性
    protected final String CPU_PeriodPath = CPU_PATH + "cpu.cfs_period_us"; // 初始化读取：一次性

    private final double allocatableCores;

    protected CgroupV1BaseProvider() throws IOException {

        validateFiles();

        // 初始化：一次性读取配额和周期（读取后自动关闭）
        long quota = readLongOnce(CPU_QuotaPath);
        long period = readLongOnce(CPU_PeriodPath);
        this.allocatableCores = calculateAllocatableCores(quota, period);
    }

    private void validateFiles() throws IOException {
        if (!Files.exists(Paths.get(CPU_AcctUsagePath))) {
            throw new IOException("cgroup v1文件不存在: " + CPU_AcctUsagePath);
        }
        if (!Files.exists(Paths.get(CPU_QuotaPath))) {
            throw new IOException("cgroup v1文件不存在: " + CPU_QuotaPath);
        }
        if (!Files.exists(Paths.get(CPU_PeriodPath))) {
            throw new IOException("cgroup v1文件不存在: " + CPU_PeriodPath);
        }
    }

    /**
     * 一次性读取文件内容（仅初始化时使用）
     */
    private long readLongOnce(String filePath) throws IOException {
        // 读取后自动关闭（Files.readAllBytes内部会处理资源释放）
        byte[] content = Files.readAllBytes(Paths.get(filePath));
        if (content.length == 0) {
            return -1;
        }
        return Long.parseLong(new String(content).trim());
    }

    private double calculateAllocatableCores(long quota, long period) {
        if (quota == -1) {
            return Runtime.getRuntime().availableProcessors();
        }
        return period == 0 ? 1.0 : (double) quota / period;
    }


    @Override
    public double getAllocatableCores() {
        return allocatableCores;
    }

}
